//
// Created by Administrator on 2020/11/2.
//

#include "../../include/l2Api.h"
#include "../l1Intf/include/l1IntfApi.h"
#include "../l3Intf/include/l3IntfApi.h"
#include "../sdap/include/sdapApi.h"
#include "../pdcp/include/pdcpApi.h"
#include "../rlc/include/rlcApi.h"
#include "../mac/include/macApi.h"
#include "../msch/include/mschApi.h"
#include "fs_0.h"

#define RAN_L2_FS_0_POLL_TIMEOUT  10

int32_t RAN_L2_FS_0_Init(void)
{
    int32_t rc;

    rc = RAN_L2_L3I_Init();
    IF_RETURN(!=, O5G_RC_OK, rc, O5G_RC_ER, "RAN_L2_FS_0_Init() RAN_L2_L3I_Init() error!\n");

    rc = RAN_L2_L1I_Init();
    IF_RETURN(!=, O5G_RC_OK, rc, O5G_RC_ER, "RAN_L2_FS_0_Init() RAN_L2_L1I_Init() error!\n");

    rc = RAN_SDAP_Init();
    IF_RETURN(!=, O5G_RC_OK, rc, O5G_RC_ER, "RAN_L2_FS_0_Init() RAN_SDAP_Init() error!\n");

    rc = RAN_PDCP_Init();
    IF_RETURN(!=, O5G_RC_OK, rc, O5G_RC_ER, "RAN_L2_FS_0_Init() RAN_PDCP_Init() error!\n");

    rc = RAN_RLC_Init();
    IF_RETURN(!=, O5G_RC_OK, rc, O5G_RC_ER, "RAN_L2_FS_0_Init() RAN_RLC_Init() error!\n");

    rc = RAN_MAC_Init();
    IF_RETURN(!=, O5G_RC_OK, rc, O5G_RC_ER, "RAN_L2_FS_0_Init() RAN_MAC_Init() error!\n");

    rc = RAN_MSCH_Init();
    IF_RETURN(!=, O5G_RC_OK, rc, O5G_RC_ER, "RAN_L2_FS_0_Init() RAN_MSCH_Init() error!\n");

    return O5G_RC_OK;
}

int32_t RAN_L2_FS_0_Final(void)
{
    int32_t rc;

    rc = RAN_SDAP_Final();
    IF_RETURN(!=, O5G_RC_OK, rc, O5G_RC_ER, "RAN_L2_FS_0_Final() RAN_SDAP_Final() error!\n");

    rc = RAN_PDCP_Final();
    IF_RETURN(!=, O5G_RC_OK, rc, O5G_RC_ER, "RAN_L2_FS_0_Final() RAN_PDCP_Final() error!\n");

    rc = RAN_RLC_Final();
    IF_RETURN(!=, O5G_RC_OK, rc, O5G_RC_ER, "RAN_L2_FS_0_Final() RAN_RLC_Final() error!\n");

    rc = RAN_MAC_Final();
    IF_RETURN(!=, O5G_RC_OK, rc, O5G_RC_ER, "RAN_L2_FS_0_Final() RAN_MAC_Final() error!\n");

    rc = RAN_MSCH_Final();
    IF_RETURN(!=, O5G_RC_OK, rc, O5G_RC_ER, "RAN_L2_FS_0_Final() RAN_MSCH_Final() error!\n");

    rc = RAN_L2_L1I_Final();
    IF_RETURN(!=, O5G_RC_OK, rc, O5G_RC_ER, "RAN_L2_FS_0_Final() RAN_L2_L1I_Final() error!\n");

    rc = RAN_L2_L3I_Final();
    IF_RETURN(!=, O5G_RC_OK, rc, O5G_RC_ER, "RAN_L2_FS_0_Final() RAN_L2_L3I_Final() error!\n");

    return O5G_RC_OK;
}

static void RAN_L2_FS_0_UL_HI_TtiProcess(int32_t event, int fd, void *data)
{
    int32_t rc;
    S_RanL2CellInst *pCellInst;

    pCellInst = (S_RanL2CellInst *)data;

    S_OsSelectInd indMsg;
    rc = OS_SelectRecvByFd(fd, &indMsg);
    IF_RETURN(!=, O5G_RC_OK, rc, NO_RETURN_VALUE, "RAN_L2_FS_0_UL_HI_TtiProcess() OS_SelectRecv() error!\n");
    IF_RETURN(!=, RAN_L2_INTRA_IND_MSG_TTI_IND, indMsg.ind, NO_RETURN_VALUE, "RAN_L2_FS_0_UL_HI_TtiProcess() recv unsupport ind[%#x]\n", indMsg.ind);

    RAN_PDCP_UL_TtiProcess(pCellInst);
    RAN_SDAP_UL_TtiProcess(pCellInst);
}

static void RAN_L2_FS_0_UL_HI_PtoProcess(S_RanL2CellInst *pCellInst)
{
    RAN_PDCP_UL_PtoProcess(pCellInst);
    RAN_SDAP_UL_PtoProcess(pCellInst);
}

/* L2 UL HIGH部分：SDAP PDCP上行 */
static void *RAN_L2_FS_0_UL_HI_Task(void* arg)
{
    int fd;
    int32_t rc;
    S_RanL2CellInst *pCellInst;

    pCellInst = (S_RanL2CellInst *)arg;

    fd = pCellInst->ulHiTtiSelector.recvFd;
    POLLER_Add(pCellInst->ulHiPoller, fd, RAN_L2_FS_0_UL_HI_TtiProcess, NULL, NULL, pCellInst);

    while (1)
    {
        rc = POLLER_Poll(pCellInst->ulHiPoller, RAN_L2_FS_0_POLL_TIMEOUT);
        if (0 == rc)
        {
            /*
             * POLL TIME OUT处理，是为了在L1 START前处理能够处理L3等消息。
             * L1 START后就会有TTI消息上来，就不会超时进入此分支。
             */
            RAN_L2_FS_0_UL_HI_PtoProcess(pCellInst);
        }
        pthread_testcancel();
    }
    return NULL;
}

static void RAN_L2_FS_0_DL_HI_TtiProcess(int32_t event, int fd, void *data)
{
    int32_t rc;
    S_RanL2CellInst *pCellInst;

    pCellInst = (S_RanL2CellInst *)data;

    S_OsSelectInd indMsg;
    rc = OS_SelectRecvByFd(fd, &indMsg);
    IF_RETURN(!=, O5G_RC_OK, rc, NO_RETURN_VALUE, "RAN_L2_FS_0_DL_HI_TtiProcess() OS_SelectRecv() error!\n");
    IF_RETURN(!=, RAN_L2_INTRA_IND_MSG_TTI_IND, indMsg.ind, NO_RETURN_VALUE, "RAN_L2_FS_0_DL_HI_TtiProcess() recv unsupport ind[%#x]\n", indMsg.ind);

    RAN_SDAP_DL_TtiProcess(pCellInst);
    RAN_PDCP_DL_TtiProcess(pCellInst);
}

static void RAN_L2_FS_0_DL_HI_PtoProcess(S_RanL2CellInst *pCellInst)
{
    RAN_SDAP_DL_PtoProcess(pCellInst);
    RAN_PDCP_DL_PtoProcess(pCellInst);
}

/* L2 DL HIGH部分：SDAP PDCP下行 */
static void *RAN_L2_FS_0_DL_HI_Task(void* arg)
{
    int fd;
    int32_t rc;
    S_RanL2CellInst *pCellInst;

    pCellInst = (S_RanL2CellInst *)arg;

    fd = pCellInst->dlHiTtiSelector.recvFd;
    POLLER_Add(pCellInst->dlHiPoller, fd, RAN_L2_FS_0_DL_HI_TtiProcess, NULL, NULL, pCellInst);

    while (1)
    {
        rc = POLLER_Poll(pCellInst->dlHiPoller, RAN_L2_FS_0_POLL_TIMEOUT);
        if (0 == rc)
        {
            /*
             * POLL TIME OUT处理，是为了在L1 START前处理能够处理L3等消息。
             * L1 START后就会有TTI消息上来，就不会超时进入此分支。
             */
            RAN_L2_FS_0_DL_HI_PtoProcess(pCellInst);
        }
        pthread_testcancel();
    }
    return NULL;
}

static void RAN_L2_FS_0_UL_LO_TtiProcess(int32_t event, int fd, void *data)
{
    int32_t rc;
    S_RanL2CellInst *pCellInst;

    pCellInst = (S_RanL2CellInst *)data;

    S_OsSelectInd indMsg;
    rc = OS_SelectRecvByFd(fd, &indMsg);
    IF_RETURN(!=, O5G_RC_OK, rc, NO_RETURN_VALUE, "RAN_L2_FS_0_UL_LO_TtiProcess() OS_SelectRecv() error!\n");
    IF_RETURN(!=, RAN_L2_INTRA_IND_MSG_TTI_IND, indMsg.ind, NO_RETURN_VALUE, "RAN_L2_FS_0_UL_LO_TtiProcess() recv unsupport ind[%#x]\n", indMsg.ind);

    RAN_MAC_UL_TtiProcess(pCellInst);
    RAN_RLC_UL_TtiProcess(pCellInst);
}

static void RAN_L2_FS_0_UL_LO_PtoProcess(S_RanL2CellInst *pCellInst)
{
    RAN_MAC_UL_PtoProcess(pCellInst);
    RAN_RLC_UL_PtoProcess(pCellInst);
}

/* L2 UL LOW部分：RLC MAC上行 */
static void *RAN_L2_FS_0_UL_LO_Task(void* arg)
{
    int fd;
    int32_t rc;
    S_RanL2CellInst *pCellInst;

    pCellInst = (S_RanL2CellInst *)arg;

    fd = pCellInst->ulLoTtiSelector.recvFd;
    POLLER_Add(pCellInst->ulLoPoller, fd, RAN_L2_FS_0_UL_LO_TtiProcess, NULL, NULL, pCellInst);

    while (1)
    {
        rc = POLLER_Poll(pCellInst->ulLoPoller, RAN_L2_FS_0_POLL_TIMEOUT);
        if (0 == rc)
        {
            /*
             * POLL TIME OUT处理，是为了在L1 START前处理能够处理L3等消息。
             * L1 START后就会有TTI消息上来，就不会超时进入此分支。
             */
            RAN_L2_FS_0_UL_LO_PtoProcess(pCellInst);
        }
        pthread_testcancel();
    }
    return NULL;
}

static void RAN_L2_FS_0_DL_LO_WakeUp(S_OsSelectInd *indMsg)
{
    indMsg->ind = RAN_L2_INTRA_IND_MSG_TTI_IND;
    OS_SelectSend(&gpRanL2L1IntfContext->dlSelector, indMsg);
}

static void RAN_L2_FS_0_DL_LO_TtiProcess(int32_t event, int fd, void *data)
{
    int32_t rc;
    S_RanL2CellInst *pCellInst;

    pCellInst = (S_RanL2CellInst *)data;

    S_OsSelectInd indMsg;
    rc = OS_SelectRecvByFd(fd, &indMsg);
    IF_RETURN(!=, O5G_RC_OK, rc, NO_RETURN_VALUE, "RAN_L2_FS_0_DL_LO_TtiProcess() OS_SelectRecv() error!\n");
    IF_RETURN(!=, RAN_L2_INTRA_IND_MSG_TTI_IND, indMsg.ind, NO_RETURN_VALUE, "RAN_L2_FS_0_DL_LO_TtiProcess() recv unsupport ind[%#x]\n", indMsg.ind);

//    CSP_LOG_INF(RAN_MD_L2_L1I, "RAN_L2_FS_0_DL_LO_TtiProcess() recv RAN_L2_INTRA_IND_MSG_TTI_IND. data[%lu]\n", indMsg.data);

    RAN_RLC_DL_TtiProcess(pCellInst);
    RAN_MAC_DL_TtiProcess(pCellInst);

    RAN_L2_FS_0_DL_LO_WakeUp(&indMsg);
}

static void RAN_L2_FS_0_DL_LO_PtoProcess(S_RanL2CellInst *pCellInst)
{
    RAN_RLC_DL_PtoProcess(pCellInst);
    RAN_MAC_DL_PtoProcess(pCellInst);
}

/* L2 DL LOW部分：RLC MAC下行 */
static void *RAN_L2_FS_0_DL_LO_Task(void* arg)
{
    int fd;
    int32_t rc;
    S_RanL2CellInst *pCellInst;

    pCellInst = (S_RanL2CellInst *)arg;

    fd = pCellInst->dlLoTtiSelector.recvFd;
    POLLER_Add(pCellInst->dlLoPoller, fd, RAN_L2_FS_0_DL_LO_TtiProcess, NULL, NULL, pCellInst);

    while (1)
    {
        rc = POLLER_Poll(pCellInst->dlLoPoller, RAN_L2_FS_0_POLL_TIMEOUT);
        if (0 == rc)
        {
            /*
             * POLL TIME OUT处理，是为了在L1 START前处理能够处理L3等消息。
             * L1 START后就会有TTI消息上来，就不会超时进入此分支。
             */
            RAN_L2_FS_0_DL_LO_PtoProcess(pCellInst);
        }
        pthread_testcancel();
    }
    return NULL;
}

static void RAN_L2_FS_0_MSCH_SCH_TtiProcess(int32_t event, int fd, void *data)
{
    int32_t rc;
    S_RanL2CellInst *pCellInst;
    S_O5gSfnSlot recvSfnSlot;

    pCellInst = (S_RanL2CellInst *)data;

    S_OsSelectInd indMsg;
    rc = OS_SelectRecvByFd(fd, &indMsg);
    IF_RETURN(!=, O5G_RC_OK, rc, NO_RETURN_VALUE, "RAN_L2_FS_0_MSCH_SCH_TtiProcess() OS_SelectRecv() error!\n");
    IF_RETURN(!=, RAN_L2_INTRA_IND_MSG_TTI_IND, indMsg.ind, NO_RETURN_VALUE, "RAN_L2_FS_0_MSCH_SCH_TtiProcess() recv unsupport ind[%#x]\n", indMsg.ind);

    recvSfnSlot.sfn = indMsg.sn >> 16;
    recvSfnSlot.slot = indMsg.sn & 0xFFFF;
    if (pCellInst->currAirSfnSlot.sfn != recvSfnSlot.sfn || pCellInst->currAirSfnSlot.slot != recvSfnSlot.slot)
    {
        CSP_LOG_WRN(RAN_MD_L2_L1I, "RAN_L2_FS_0_MSCH_SCH_TtiProcess() sfn is not coutinue! airSfnSlot[%u, %u], recvSfnSlot[%u, %u]\n",
            pCellInst->currAirSfnSlot.sfn, pCellInst->currAirSfnSlot.slot, recvSfnSlot.sfn, recvSfnSlot.slot);
        return;
    }

    RAN_MSCH_SCH_TtiProcess(pCellInst);
}

static void RAN_L2_FS_0_MSCH_SCH_PtoProcess(S_RanL2CellInst *pCellInst)
{
    RAN_MSCH_SCH_PtoProcess(pCellInst);
}

/* MSCH调度线程 */
static void *RAN_L2_FS_0_MSCH_SCH_Task(void* arg)
{
    int fd;
    int32_t rc;
    S_RanL2CellInst *pCellInst;

    pCellInst = (S_RanL2CellInst *)arg;

    fd = pCellInst->mschSchTtiSelector.recvFd;
    POLLER_Add(pCellInst->mschSchPoller, fd, RAN_L2_FS_0_MSCH_SCH_TtiProcess, NULL, NULL, pCellInst);

    while (1)
    {
        rc = POLLER_Poll(pCellInst->mschSchPoller, RAN_L2_FS_0_POLL_TIMEOUT);
        if (0 == rc)
        {
            /*
             * POLL TIME OUT处理，是为了在L1 START前处理能够处理L3等消息。
             * L1 START后就会有TTI消息上来，就不会超时进入此分支。
             */
            RAN_L2_FS_0_MSCH_SCH_PtoProcess(pCellInst);
        }
        pthread_testcancel();
    }
    return NULL;
}

static void RAN_L2_FS_0_MSCH_ALG_TtiProcess(int32_t event, int fd, void *data)
{
    int32_t rc;
    S_RanL2CellInst *pCellInst;
    S_O5gSfnSlot recvSfnSlot;

    pCellInst = (S_RanL2CellInst *)data;

    S_OsSelectInd indMsg;
    rc = OS_SelectRecvByFd(fd, &indMsg);
    IF_RETURN(!=, O5G_RC_OK, rc, NO_RETURN_VALUE, "RAN_L2_FS_0_MSCH_ALG_TtiProcess() OS_SelectRecv() error!\n");
    IF_RETURN(!=, RAN_L2_INTRA_IND_MSG_TTI_IND, indMsg.ind, NO_RETURN_VALUE, "RAN_L2_FS_0_MSCH_ALG_TtiProcess() recv unsupport ind[%#x]\n", indMsg.ind);

    recvSfnSlot.sfn = indMsg.sn >> 16;
    recvSfnSlot.slot = indMsg.sn & 0xFFFF;
    if (pCellInst->currAirSfnSlot.sfn != recvSfnSlot.sfn || pCellInst->currAirSfnSlot.slot != recvSfnSlot.slot)
    {
        CSP_LOG_WRN(RAN_MD_L2_L1I, "RAN_L2_FS_0_MSCH_ALG_TtiProcess() sfn is not coutinue! airSfnSlot[%u, %u], recvSfnSlot[%u, %u]\n",
            pCellInst->currAirSfnSlot.sfn, pCellInst->currAirSfnSlot.slot, recvSfnSlot.sfn, recvSfnSlot.slot);
        return;
    }

    RAN_MSCH_ALG_TtiProcess(pCellInst);
}

static void RAN_L2_FS_0_MSCH_ALG_PtoProcess(S_RanL2CellInst *pCellInst)
{
    RAN_MSCH_ALG_PtoProcess(pCellInst);
}

/* MSCH算法线程 */
static void *RAN_L2_FS_0_MSCH_ALG_Task(void* arg)
{
    int fd;
    int32_t rc;
    S_RanL2CellInst *pCellInst;

    pCellInst = (S_RanL2CellInst *)arg;

    fd = pCellInst->mschAlgTtiSelector.recvFd;
    POLLER_Add(pCellInst->mschAlgPoller, fd, RAN_L2_FS_0_MSCH_ALG_TtiProcess, NULL, NULL, pCellInst);

    while (1)
    {
        rc = POLLER_Poll(pCellInst->mschAlgPoller, RAN_L2_FS_0_POLL_TIMEOUT);
        if (0 == rc)
        {
            /*
             * POLL TIME OUT处理，是为了在L1 START前处理能够处理L3等消息。
             * L1 START后就会有TTI消息上来，就不会超时进入此分支。
             */
            RAN_L2_FS_0_MSCH_ALG_PtoProcess(pCellInst);
        }
        pthread_testcancel();
    }
    return NULL;
}

int32_t RAN_L2_FS_0_Start(void)
{
    int32_t rc;
    uint32_t nbLoop;
    uint32_t cellLoop;
    S_RanL2NbInst *pNbInst;
    S_RanL2CellInst *pCellInst;

    rc = RAN_L2_L3I_Start();
    IF_RETURN(!=, O5G_RC_OK, rc, O5G_RC_ER, "RAN_L2_FS_0_Start() RAN_L2_L3I_Start() error!\n");

    rc = RAN_L2_L1I_Start();
    IF_RETURN(!=, O5G_RC_OK, rc, O5G_RC_ER, "RAN_L2_FS_0_Start() RAN_L2_L1I_Start() error!\n");

    for (nbLoop = 0; nbLoop < RAN_NODEB_NUM_MAX; nbLoop++)
    {
        pNbInst = &gpRanL2Context->nbInst[nbLoop];
        IF_CONTINUE(==, FALSE, pNbInst->inUse, NO_LOG_MESSAGE);
        for (cellLoop = 0; cellLoop < RAN_CELL_NUM_MAX; cellLoop++)
        {
            pCellInst = &pNbInst->cellInst[cellLoop];
            IF_CONTINUE(==, FALSE, pCellInst->inUse, NO_LOG_MESSAGE);

            rc = OS_ThreadCreate(&pCellInst->ulHiTid, RAN_L2_FS_0_UL_HI_Task, pCellInst);
            IF_RETURN(!=, O5G_RC_OK, rc, O5G_RC_ER, "RAN_L2_FS_0_Start() OS_ThreadCreate(ulHiTid) error!\n");
            rc = OS_ThreadCreate(&pCellInst->dlHiTid, RAN_L2_FS_0_DL_HI_Task, pCellInst);
            IF_RETURN(!=, O5G_RC_OK, rc, O5G_RC_ER, "RAN_L2_FS_0_Start() OS_ThreadCreate(dlHiTid) error!\n");

            rc = OS_ThreadCreate(&pCellInst->ulLoTid, RAN_L2_FS_0_UL_LO_Task, pCellInst);
            IF_RETURN(!=, O5G_RC_OK, rc, O5G_RC_ER, "RAN_L2_FS_0_Start() OS_ThreadCreate(ulLoTid) error!\n");
            rc = OS_ThreadCreate(&pCellInst->dlLoTid, RAN_L2_FS_0_DL_LO_Task, pCellInst);
            IF_RETURN(!=, O5G_RC_OK, rc, O5G_RC_ER, "RAN_L2_FS_0_Start() OS_ThreadCreate(dlLoTid) error!\n");

            rc = OS_ThreadCreate(&pCellInst->mschSchTid, RAN_L2_FS_0_MSCH_SCH_Task, pCellInst);
            IF_RETURN(!=, O5G_RC_OK, rc, O5G_RC_ER, "RAN_L2_FS_0_Start() OS_ThreadCreate(mschSchTid) error!\n");
            rc = OS_ThreadCreate(&pCellInst->mschAlgTid, RAN_L2_FS_0_MSCH_ALG_Task, pCellInst);
            IF_RETURN(!=, O5G_RC_OK, rc, O5G_RC_ER, "RAN_L2_FS_0_Start() OS_ThreadCreate(mschAlgTid) error!\n");
        }
    }

    return O5G_RC_OK;
}

int32_t RAN_L2_FS_0_Stop(void)
{
    int32_t rc;
    uint32_t nbLoop;
    uint32_t cellLoop;
    S_RanL2NbInst *pNbInst;
    S_RanL2CellInst *pCellInst;

    rc = RAN_L2_L3I_Stop();
    IF_RETURN(!=, O5G_RC_OK, rc, O5G_RC_ER, "RAN_L2_FS_0_Stop() RAN_L2_L3I_Stop() error!\n");

    rc = RAN_L2_L1I_Stop();
    IF_RETURN(!=, O5G_RC_OK, rc, O5G_RC_ER, "RAN_L2_FS_0_Stop() RAN_L2_L1I_Stop() error!\n");

    for (nbLoop = 0; nbLoop < RAN_NODEB_NUM_MAX; nbLoop++)
    {
        pNbInst = &gpRanL2Context->nbInst[nbLoop];
        IF_CONTINUE(==, FALSE, pNbInst->inUse, NO_LOG_MESSAGE);
        for (cellLoop = 0; cellLoop < RAN_CELL_NUM_MAX; cellLoop++)
        {
            pCellInst = &pNbInst->cellInst[cellLoop];
            IF_CONTINUE(==, FALSE, pCellInst->inUse, NO_LOG_MESSAGE);

            rc = OS_ThreadCancel(pCellInst->ulHiTid);
            IF_RETURN(!=, O5G_RC_OK, rc, O5G_RC_ER, "RAN_L2_FS_0_Stop() OS_ThreadCancel(ulHiTid) error!\n");
            rc = OS_ThreadCancel(pCellInst->dlHiTid);
            IF_RETURN(!=, O5G_RC_OK, rc, O5G_RC_ER, "RAN_L2_FS_0_Stop() OS_ThreadCancel(dlHiTid) error!\n");

            rc = OS_ThreadCancel(pCellInst->ulLoTid);
            IF_RETURN(!=, O5G_RC_OK, rc, O5G_RC_ER, "RAN_L2_FS_0_Stop() OS_ThreadCancel(ulLoTid) error!\n");
            rc = OS_ThreadCancel(pCellInst->dlLoTid);
            IF_RETURN(!=, O5G_RC_OK, rc, O5G_RC_ER, "RAN_L2_FS_0_Stop() OS_ThreadCancel(dlLoTid) error!\n");

            rc = OS_ThreadCancel(pCellInst->mschSchTid);
            IF_RETURN(!=, O5G_RC_OK, rc, O5G_RC_ER, "RAN_L2_FS_0_Stop() OS_ThreadCancel(mschSchTid) error!\n");
            rc = OS_ThreadCancel(pCellInst->mschAlgTid);
            IF_RETURN(!=, O5G_RC_OK, rc, O5G_RC_ER, "RAN_L2_FS_0_Stop() OS_ThreadCancel(mschAlgTid) error!\n");
        }
    }

    return O5G_RC_OK;
}
